#include <inctypes.h>
#include <incctrls.h>

/*********************************************************************************
 * 
 * krl_sem_init - 初始化一个信号量
 * 
 * DESCRIPTION
 */

void krl_sem_init
    (
    sem_t * sem
    )
    {
    cpuflg_t cpuflg;
    
    /* 初始化锁 */

    hal_spinlock_init (&sem->lock);

    /* 初始化等待队列链表 */

    waitlst_t_init (&sem->wlist);

    /* 自旋锁 加锁 */

    spinlock_saveflg_cli (&sem->lock, &cpuflg);

    sem->count = 0; /* 初始化 信号量计数值 */
    sem->flag  = 0; /* 初始化 信号量标志位 */

    /* 自旋锁 解锁 */

    spinunlock_restflg_sti (&sem->lock, &cpuflg);

    return;
    }

/*********************************************************************************
 * 
 * krl_sem_take - 获取一个信号量
 * 
 * DESCRIPTION
 */

void krl_sem_take
    (
    sem_t * sem
    )
    {
    cpuflg_t cpuflg;

start_up:
    
    /* 自旋锁 加锁 */

    spinlock_saveflg_cli (&sem->lock, &cpuflg);

    /* 如果信号量值小于1, 就让当前进程进入等待状态 */

    if (sem->count < 1)
        {

        /* 将当前线程挂入等待队列 */

        krl_wtlst_add_thread (&sem->wlist);

        /* 自旋锁 解锁 */

        spinunlock_restflg_sti (&sem->lock, &cpuflg);

        /* 调度并切换到新的进程运行, 下次这个进程运行时会从下面goto开始 */

        krl_sched_run ();

        /* 重新检查信号量, 防止其他代码已经改了信号量的计数值 */

        goto start_up;
        }

    /* 信号量值减1 */

    sem->count--;

    /* 自旋锁 解锁 */

    spinunlock_restflg_sti (&sem->lock, &cpuflg);

    return;
    }

/*********************************************************************************
 * 
 * krl_sem_give - 释放一个信号量
 * 
 * DESCRIPTION
 */

void krl_sem_give 
    (
    sem_t * sem
    )
    {
    cpuflg_t cpuflg;

    /* 自旋锁 加锁 */

    spinlock_saveflg_cli (&sem->lock, &cpuflg);

    /* 信号量计数值 +1 */

    sem->count++;

    /* 判断值的有效性 (仍然小于1, 则数据结构体故障) */

    if (sem->count < 1)
        {
        
        /* 自旋锁 解锁 */

        spinunlock_restflg_sti (&sem->lock, &cpuflg);
        hal_system_die ("sem give err\r\n");
        }

    /* 唤醒等待队列上的所有进程 */

    krl_wlst_allwakeup (&sem->wlist);

    /* 自旋锁 解锁 */

    spinunlock_restflg_sti (&sem->lock, &cpuflg);

    /* 设置需要调度的标志 */

    krl_sched_setflg ();

    return;
    }    

   
